using UnityEngine;

// Graph 类，挂载在某个 GameObject 上，用于在场景中生成并控制一系列表示函数图像的点
public class Graph : MonoBehaviour
{

	// ======================
	// SerializeField 字段（可在 Inspector 中编辑，且即使为 private 也会显示）
	// ======================

	// pointPrefab：点的预制体，代表网格中的每一个“点”，是一个 Transform 类型的预制体
	// 通常是一个空物体或者带有小球 Mesh 的物体，用于在场景中显示位置
	[SerializeField]
	Transform pointPrefab;

	// resolution：网格的分辨率，即在 U 和 V 方向上各有多少个点
	// 取值范围在 Inspector 中被限制为 10 ~ 100，可通过 Range 属性设置
	[SerializeField, Range(10, 100)]
	int resolution = 10; // 默认 10x10 的点阵

	// function：一个枚举值，表示要绘制的数学函数类型
	// 枚举定义在 FunctionLibrary 类中，比如可能是 Sin、Wave、Saddle 等
	[SerializeField]
	FunctionLibrary.FunctionName function;

	// ======================
	// 私有成员变量
	// ======================

	// points：一个 Transform 数组，用于存储所有生成的“点”（即点 Prefab 的实例）
	Transform[] points;

	// ======================
	// Unity 生命周期方法
	// ======================

	// Awake()：在脚本实例被加载时调用，一般用于初始化，只调用一次
	void Awake()
	{
		// step：每个点之间的间隔距离
		// 整个网格是在一个 -1 ~ 1 的范围内均匀分布的，所以总长度为 2，除以 resolution 得到步长
		float step = 2f / resolution;

		// scale：每个点的缩放大小，这里设置为与步长相等，让点之间的间距视觉上更匹配
		var scale = Vector3.one * step;

		// 初始化 points 数组，总点数是 resolution * resolution（即一个正方形网格）
		points = new Transform[resolution * resolution];

		// 循环创建每一个点（即实例化 pointPrefab）
		for (int i = 0; i < points.Length; i++)
		{
			// 实例化一个点 Prefab，作为当前网格中的一个点
			Transform point = points[i] = Instantiate(pointPrefab);

			// 设置这个点的本地缩放，让它大小与步长一致
			point.localScale = scale;

			// 将这个点设置为当前 GameObject（Graph）的子物体，false 表示不改变其在世界空间中的位置
			point.SetParent(transform, false);
		}
	}

	// Update()：每帧调用一次，用于更新每个点的位置
	void Update()
	{
		// 从 FunctionLibrary 获取当前选中的函数（根据 function 枚举值）
		FunctionLibrary.Function f = FunctionLibrary.GetFunction(function);

		// 获取当前时间，用于实现动态效果（比如波动、动画等）
		float time = Time.time;

		// 同样计算出每个点之间的步长
		float step = 2f / resolution;

		// v：初始化为网格在“垂直方向”（V轴，可以理解为 Y 或 Z 轴上的起始偏移）
		// 0.5f * step 是为了居中，-1f 是将范围从 [0, resolution] 映射到 [-1, 1]
		float v = 0.5f * step - 1f;

		// 下面的循环用于遍历所有点，并为每个点计算其在空间中的位置
		for (int i = 0, x = 0, z = 0; i < points.Length; i++, x++)
		{
			// 当 x 达到 resolution 时，说明当前行已经结束，转到下一行
			if (x == resolution)
			{
				x = 0;          // 重置 x 为 0，开始新的一行
				z += 1;         // 增加 z（行号）
				v = (z + 0.5f) * step - 1f; // 重新计算 v，对应新行的中心位置
			}

			// u：当前点在“水平方向”（U轴，可以理解为 X 或 Z 轴）的位置
			// (x + 0.5f) * step - 1f 是为了将索引映射到 [-1, 1] 范围内，并居中
			float u = (x + 0.5f) * step - 1f;

			// 调用函数 f(u, v, time)，传入当前点的 u, v 坐标和时间，返回一个 Vector3 位置
			// 然后将这个位置赋值给当前点（points[i]）的本地坐标
			points[i].localPosition = f(u, v, time);
		}
	}
}